/*
 *  jmp.c
 *
 *  Created on: 2010-5-12
 *      Author: liuyond
 *    Abstract: this file will implement the simulation of the following instruction
 *    			JMP, CALL, JE/JNE, JZ/JNZ, JCC, LOOP, RET ...
 */
#include "cpu.h"
#include <stdio.h>

void JMP_Jv(cpu_t *cpu, instruction_t *i)
{
	short tmp = i->operand.imm;
	cpu->ip += tmp;
}

void JMP_Ap(cpu_t *cpu, instruction_t *i)
{
	short seg = i->operand.imm >> 16;
	short tmp = i->operand.imm & 0xFFFF;
	cpu_set_seg(cpu, CS, seg);
	cpu->ip += tmp;
}

void JMP_Jb(cpu_t *cpu, instruction_t *i)
{
	char tmp = i->operand.imm;
	cpu->ip += tmp;
}

void JCC_Jb(cpu_t *cpu, instruction_t *i)
{
	char tmp, condition = 0;

	switch (i->opcode & 0xF)
	{
		case 0x00: /* JO */
			condition = cpu_get_flag(cpu, OF);
			break;
		case 0x01: /* JNO */
			condition = !cpu_get_flag(cpu, OF);
			break;
		case 0x02: /* JB */
			condition = cpu_get_flag(cpu, CF);
			break;
		case 0x03: /* JNB */
			condition = !cpu_get_flag(cpu, CF);
			break;
		case 0x04: /* JZ */
			condition = cpu_get_flag(cpu, ZF);
			break;
		case 0x05: /* JNZ */
			condition = !cpu_get_flag(cpu, ZF);
			break;
		case 0x06: /* JBE */
			condition = cpu_get_flag(cpu, CF) || cpu_get_flag(cpu, ZF);
			break;
		case 0x07: /* JNBE */
			condition = !cpu_get_flag(cpu, CF) && !cpu_get_flag(cpu, ZF);
			break;
		case 0x08: /* JS */
			condition = cpu_get_flag(cpu, SF);
			break;
		case 0x09: /* JNS */
			condition = !cpu_get_flag(cpu, SF);
			break;
		case 0x0A: /* JP */
			condition = cpu_get_flag(cpu, PF);
			break;
		case 0x0B: /* JNP */
			condition = !cpu_get_flag(cpu, PF);
			break;
		case 0x0C: /* JL */
			condition = (cpu_get_flag(cpu, SF) != cpu_get_flag(cpu, OF));
			break;
		case 0x0D: /* JNL */
			condition = (cpu_get_flag(cpu, SF) == cpu_get_flag(cpu, OF));
			break;
		case 0x0E: /* JLE */
			condition = cpu_get_flag(cpu, ZF) || (cpu_get_flag(cpu, SF) != cpu_get_flag(cpu, OF));
			break;
		case 0x0F: /* JNLE */
			condition = (cpu_get_flag(cpu, SF) == cpu_get_flag(cpu, OF)) && !cpu_get_flag(cpu, ZF); 
			break;
	}
	
	if (condition)
	{
		tmp = i->operand.imm;
		cpu->ip += tmp;
	}
}

void JCXZ_Jb(cpu_t *cpu, instruction_t *i)
{
	// jmp if cx register is zero
	char tmp;

	if (cpu_get_reg(cpu, CX))
	{
		tmp = i->operand.imm;
		cpu->ip += tmp;
	}
}

void LOOPNZ_Jb(cpu_t *cpu, instruction_t *i)
{
	char tmp;
	word count = cpu_get_reg(cpu, CX);

	if ((--count != 0) && !cpu_get_flag(cpu, ZF))
	{
		tmp = i->operand.imm;
		cpu->ip += tmp;
	}
	cpu_set_reg(cpu, CX, count);
}

void LOOPZ_Jb(cpu_t *cpu, instruction_t *i)
{
	char tmp;
	word count = cpu_get_reg(cpu, CX);

	if ((--count != 0) && cpu_get_flag(cpu, ZF))
	{
		tmp = i->operand.imm;
		cpu->ip += tmp;
	}
	cpu_set_reg(cpu, CX, count);
}

void LOOP_Jb(cpu_t *cpu, instruction_t *i)
{
	char tmp;
	word count = cpu_get_reg(cpu, CX);

	if (--count != 0)
	{
		tmp = i->operand.imm;
		cpu->ip += tmp;
	}
	cpu_set_reg(cpu, CX, count);
}

void CALL_Ap(cpu_t *cpu, instruction_t *i)
{
	int seg = cpu_get_seg(cpu, CS);
	int tmp = i->operand.imm & 0xFFFF;
	cpu_push_mem_word(cpu, seg);
	cpu_push_mem_word(cpu, cpu->ip);
	seg = i->operand.imm >> 16;
	cpu_set_seg(cpu, CS, seg);
	cpu->ip += tmp;
}

void CALL_Jv(cpu_t *cpu, instruction_t *i)
{
	short tmp = i->operand.imm;
	cpu_push_mem_word(cpu, cpu->ip);
	cpu->ip += tmp;
}

void RET_Iw(cpu_t *cpu, instruction_t *i)
{
	int tmp = i->operand.imm;
	cpu->ip = cpu_pop_mem_word(cpu);
	tmp += cpu_get_reg(cpu, SP);
	cpu_set_reg(cpu, SP, tmp);
}

void RET(cpu_t *cpu, instruction_t *i)
{
	cpu->ip = cpu_pop_mem_word(cpu);
}

void RETF_Iw(cpu_t *cpu, instruction_t *i)
{
	int tmp = i->operand.imm;
	int seg = cpu_pop_mem_word(cpu);
	cpu_set_seg(cpu, CS, seg);
	cpu->ip = cpu_pop_mem_word(cpu);
	tmp += cpu_get_reg(cpu, SP);
	cpu_set_reg(cpu, SP, tmp);
}
void RETF(cpu_t *cpu, instruction_t *i)
{
	int seg = cpu_pop_mem_word(cpu);
	cpu_set_seg(cpu, CS, seg);
	cpu->ip = cpu_pop_mem_word(cpu);
}
void INT3(cpu_t *cpu, instruction_t *i)
{
	fprintf(stdout, "INT3 not implemented!\n");
}
void INT_Ib(cpu_t *cpu, instruction_t *i)
{
	fprintf(stdout, "INT_Ib not implemented!\n");
}
void INTO(cpu_t *cpu, instruction_t *i)
{
	fprintf(stdout, "INTO not implemented!\n");
}

void IRET(cpu_t *cpu, instruction_t *i)
{
	fprintf(stdout, "IRET not implemented!\n");
}
